package nl.boektrust.btsoep.server.wsdl;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.Enumeration;
import java.util.StringTokenizer;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.MimeHeaders;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class BrowseSoapServlet extends HttpServlet {
	private static final long serialVersionUID = 4530271940078554946L;
	private static final Logger logger = LoggerFactory
			.getLogger(BrowseSoapServlet.class);

	/**
	 * doGet returns the WSDL and XSD files to be handled by the SOAP via
	 * doPost. Obsolete, use
	 * http://frans.webhop.org:8080/jaxws/wsgen/wsdl/BrowseWsdlService.wsdl
	 */
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		// TODO use white list of downloadable resources?
		// NOTE files can not be read on GAE but classpath resources can.
		// TODO how to get content type?
		// TODO or not? replace live the WSDL its soap:address location into the
		// doPost() address.
		// Works with full names under /war/WEB-INF/classes and also
		// http://127.0.0.1:8888/org/slf4j/LoggerFactory.class
		logger.info("req.getScheme()=" + req.getScheme());
		logger.info("req.getServerName()=" + req.getServerName());
		logger.info("req.getServerPort()=" + req.getServerPort());
		logger.info("req.getContextPath()=" + req.getContextPath());
		logger.info("req.getServletPath()=" + req.getServletPath());
		logger.info("req.getPathInfo()=" + req.getPathInfo());
		logger.info("req.getQueryString()=" + req.getQueryString());
		// req.getPathInfo()=/BrowseWsdlService.wsdl
		String resourceName = req.getPathInfo();
		logger.info("resourceName=" + resourceName);
		if (resourceName == null || resourceName.length() < 1) {
			resp.sendError(HttpServletResponse.SC_NOT_FOUND);
			return;
		}
		resourceName = resourceName.substring(1);
		logger.info("resourceName=" + resourceName);
		URL url = null;
		// NOTE: does not seem to work: url =
		// getClass().getResource(resourceName);
		url = Thread.currentThread().getContextClassLoader()
				.getResource(resourceName);
		if (url == null) {
			resp.sendError(HttpServletResponse.SC_NOT_FOUND);
			return;
		}
		URLConnection conn = url.openConnection();
		InputStream in = conn.getInputStream();
		if (in == null) {
			logger.error("url=" + url + "; in=" + in);
			resp.sendError(HttpServletResponse.SC_NOT_FOUND);
			return;
		}
		String contentType = conn.getContentType();
		OutputStream out = resp.getOutputStream();
		// TODO: make contentType a getQueryString:
		if (contentType == null)
			contentType = "text/xml;charset=\"utf-8\"";
		resp.setStatus(HttpServletResponse.SC_OK);
		resp.setContentType(contentType);
		try {
			copy(in, out);
		} catch (Exception e) {
			logger.error("copy():", e);
			resp.sendError(HttpServletResponse.SC_NOT_FOUND);
			return;
		}
		logger.info("url=" + url);
		out.flush();
	}

	@Override
	public void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws IOException {
		try {
			// Get all the headers from the HTTP request
			MimeHeaders headers = getHeaders(req);
			// Construct a SOAPMessage from the XML in the request body
			InputStream is = req.getInputStream();
			// NOTE: GAE error=java.lang.NoClassDefFoundError: Could not
			// initialize class com.sun.xml.messaging.saaj.soap.SOAPDocumentImpl
			// but OK for
			// https://soaponix.appspot.com/BrowseSoapServlet/com/sun/xml/messaging/saaj/soap/SOAPDocumentImpl.class
			// NOTE: both messageFactory and browseSoapHandler could be outside
			// function but trying to solve missing class above
			MessageFactory  messageFactory = MessageFactory.newInstance();
			SOAPMessage soapRequest = messageFactory.createMessage(headers, is);
			// Handle soapReqest
			BrowseSoapHandler browseSoapHandler = new BrowseSoapHandler();
			SOAPMessage soapResponse = browseSoapHandler
					.handleSOAPRequest(soapRequest);
			// Write to HttpServeltResponse
			resp.setStatus(HttpServletResponse.SC_OK);
			resp.setContentType("text/xml;charset=\"utf-8\"");
			OutputStream os = resp.getOutputStream();
			soapResponse.writeTo(os);
			os.flush();
		} catch (SOAPException e) {
			logger.error("Exception while creating SOAP message:", e);
			throw new IOException("Exception while creating SOAP message.", e);
		}
	}

	@SuppressWarnings("unchecked")
	static MimeHeaders getHeaders(HttpServletRequest req) {
		Enumeration headerNames = req.getHeaderNames();
		MimeHeaders headers = new MimeHeaders();
		while (headerNames.hasMoreElements()) {
			String headerName = (String) headerNames.nextElement();
			String headerValue = req.getHeader(headerName);
			StringTokenizer values = new StringTokenizer(headerValue, ",");
			while (values.hasMoreTokens()) {
				headers.addHeader(headerName, values.nextToken().trim());
			}
		}
		return headers;
	}

	public static void copy(InputStream in, OutputStream out)
			throws IOException {
		byte[] b = new byte[4 * 1024];
		int read;
		while ((read = in.read(b)) != -1) {
			out.write(b, 0, read);
		}
	}
}
